#include <windows.h>
#include <iostream>
#include <fstream>
#include <string>

using namespace std;

void quit(const char* device) {
	cerr << device  << ": No such file or directory" << endl;
	exit(-1);
}

void oneLineNoResponse(HANDLE hSerial, int charsExpected, char firstChar) {
	bool counting = false;
	char buffer[2] = {0};
	DWORD numBytes = 0;
	while (charsExpected > 0) {
		ReadFile(hSerial, buffer, 1, &numBytes, NULL);
		if (buffer[0] == firstChar) {counting = true;}
		if (counting) {--charsExpected;}
		cout << buffer[0] << flush;
	}
}

void getAllResponses(HANDLE hSerial, int linesExpected) {
	char buffer[2] = {0};
	char lastChar = '\0';
	int linesReceived = 0;
	DWORD numBytes = 0;
	while (linesReceived < linesExpected) {
		ReadFile(hSerial, buffer, 1, &numBytes, NULL);
		if ((lastChar == '\n' || lastChar == '\0') && buffer[0] == '>') {++linesReceived;}
		cout << buffer[0] << flush;
		lastChar = buffer[0];
	}
}

void fix_com(char* port, char* result) {
	//cout << "port: " << port << endl;

	if (port[0] != 'C' || port[1] != 'O' || port[2] != 'M') {
		cout << port << " is not a valid serial port name" << endl;
		exit(-1);
	}
	
	char* num_part = port + 3;
	int port_num = atoi(num_part);
	//cout << "port_num:" << port_num << endl;
	//cout << "num_part:" << num_part << endl;

	if (port_num > 9) {
		strcpy(result+4,port);
		result[0] = '\\';
		result[1] = '\\';
		result[2] = '.';
		result[3] = '\\';
	} else {
		strcpy(result, port);
	}

	//cout << "result:" << result << endl;
}

int main(int argc, char* argv[]) {
	if (argc < 3) {
		cout << "Usage: fileToNxt COM[portNum] (-checkPort | filename) [-quit]" << endl;
		return 1;
	}

	char device[100];

	fix_com(argv[1], device);

	HANDLE hSerial = CreateFile(device, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);

	//cout << "hSerial: " << hSerial << endl;
	if (hSerial == INVALID_HANDLE_VALUE) {
		/*
		if (GetLastError()==ERROR_FILE_NOT_FOUND) {
			cout << "Device not present" << endl;
		}
		*/
		quit(device);
	}

	if (!strcmp(argv[2], "-checkPort")) {
		CloseHandle(hSerial);
		return 0;
	}

	DCB dcbSerialParams = {0};
	dcbSerialParams.DCBlength = sizeof(dcbSerialParams);

	if (!GetCommState(hSerial, &dcbSerialParams)) {
		CloseHandle(hSerial);
		quit(device);
	}

	dcbSerialParams.BaudRate = CBR_9600;
	dcbSerialParams.ByteSize = 8;
	dcbSerialParams.StopBits = ONESTOPBIT;
	dcbSerialParams.Parity = NOPARITY;

	if (!SetCommState(hSerial, &dcbSerialParams)) {
		CloseHandle(hSerial);
		quit(device);
	}

	// Consider introducing timeouts here

	ifstream inFile;
	inFile.open(argv[2], ios::in);

	int linesSent = 0, charsSent = 0;
	char buffer[2] = {0};
	char firstChar = '\0';
	DWORD numBytes = 0;
	while (!inFile.eof()) {
		inFile.get(buffer[0]);
		if (firstChar == '\0') {firstChar = buffer[0];}
		WriteFile(hSerial, buffer, 1, &numBytes, NULL);
		++charsSent;
		if (buffer[0] == '\n') {++linesSent;}
	}

	if (argc == 4) {
		oneLineNoResponse(hSerial, charsSent - linesSent, firstChar);
	} else {
		getAllResponses(hSerial, linesSent);
	}

	CloseHandle(hSerial);
	return 0;
}